<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>初学Ajax笔记</title>
    </head>
<body>
    <h1>初学Ajax笔记</h1>

    <h2>一、Ajax简介</h2>
    <h3>（一）定义</h3>
    <p>Ajax即“Asynchronous JavaScript and XML”（异步的JavaScript和XML）。它是一种在无需重新加载整个网页的情况下，能够更新部分网页的技术，通过在后台与服务器进行少量数据交换，使网页实现异步更新。这意味着可以在不影响用户操作的前提下，对页面的局部内容进行刷新，极大地提升了用户体验。例如，在一个社交网站中，用户点赞的操作无需刷新整个页面，点赞数就能实时更新，这背后很可能就运用了Ajax技术。</p>
    <h3>（二）优势</h3>
    <ul>
        <li>提升用户体验：无需整页刷新，减少等待时间，操作更加流畅。如电商网站的商品筛选功能，用户选择不同的筛选条件后，页面能迅速展示符合条件的商品，无需重新加载整个页面。</li>
        <li>减轻服务器负担：只传输必要的数据，而非整个页面数据。例如在一个搜索功能中，用户输入关键词后，通过Ajax请求，服务器只需返回与关键词相关的搜索结果数据，而非整个搜索页面的所有内容。</li>
        <li>异步交互：允许在后台执行任务，不阻塞用户界面的操作。用户可以在网页进行数据加载的同时进行其他操作，如在网页加载地图数据时，用户可以同时滚动页面查看其他信息。</li>
    </ul>

    <h2>二、Ajax核心对象：XMLHttpRequest</h2>
    <h3>（一）创建对象</h3>
    <p>在JavaScript中，创建XMLHttpRequest对象的方式因浏览器而异。在现代浏览器（IE7+、Firefox、Chrome、Safari、Opera）中，可以直接使用 <code>new XMLHttpRequest()</code> 来创建。例如：</p>
    <pre>
        <code>
let xhr = new XMLHttpRequest();
        </code>
    </pre>
    <p>对于较老的IE浏览器（IE5和IE6），则需要使用ActiveXObject来创建：</p>
    <pre>
        <code>
let xhr;
if (window.ActiveXObject) {
    xhr = new ActiveXObject("Microsoft.XMLHTTP");
} else if (window.XMLHttpRequest) {
    xhr = new XMLHttpRequest();
}
        </code>
    </pre>
    <h3>（二）对象的属性</h3>
    <ol>
        <li>
            <p><strong>readyState</strong>：表示请求的状态，取值范围为0 - 4。</p>
            <ul>
                <li>0（未初始化）：对象已创建，但尚未调用 <code>open()</code> 方法。</li>
                <li>1（启动）：已调用 <code>open()</code> 方法，但尚未调用 <code>send()</code> 方法。</li>
                <li>2（发送）：已调用 <code>send()</code> 方法，并且已接收到部分响应头信息。</li>
                <li>3（接收）：正在接收响应体数据。</li>
                <li>4（完成）：响应已完全接收，可获取完整的响应数据。</li>
            </ul>
        </li>
        <li><strong>status</strong>：表示HTTP状态码，如200表示成功，404表示未找到页面，500表示服务器内部错误等。</li>
        <li><strong>responseText</strong>：以文本形式返回服务器的响应数据。例如，如果服务器返回的是一段JSON格式的字符串数据，可通过 <code>xhr.responseText</code> 获取，然后再使用 <code>JSON.parse()</code> 方法将其转换为JavaScript对象。</li>
        <li><strong>responseXML</strong>：如果服务器返回的是XML格式的数据，可通过该属性获取解析后的XML文档对象。例如：</li>
    </ol>
    <pre>
        <code>
let xmlDoc = xhr.responseXML;
let nodes = xmlDoc.getElementsByTagName("book");
for (let i = 0; i &lt; nodes.length; i++) {
    console.log(nodes[i].getElementsByTagName("title")[0].childNodes[0].nodeValue);
}
        </code>
    </pre>
    <h3>（三）对象的方法</h3>
    <ol>
        <li>
            <p><strong>open(method, url, async)</strong>：初始化一个请求。</p>
            <ul>
                <li><code>method</code>：指定请求的HTTP方法，如"GET"、"POST"等。</li>
                <li><code>url</code>：指定请求的URL地址。</li>
                <li><code>async</code>：指定请求是否为异步，默认为 <code>true</code>。如果设置为 <code>false</code>，则为同步请求，在请求完成之前，浏览器会处于阻塞状态，直到接收到服务器的响应。</li>
            </ul>
            <p>例如：</p>
            <pre>
                <code>
xhr.open("GET", "data.txt", true);
                </code>
            </pre>
        </li>
        <li>
            <p><strong>send([body])</strong>：发送请求。对于GET请求，<code>body</code> 参数一般为空；对于POST请求，<code>body</code> 参数为要发送到服务器的数据。例如：</p>
            <pre>
                <code>
// GET请求
xhr.open("GET", "data.txt", true);
xhr.send();
// POST请求
xhr.open("POST", "submit.php", true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
let data = "name=John&age=30";
xhr.send(data);
                </code>
            </pre>
        </li>
        <li>
            <p><strong>setRequestHeader(header, value)</strong>：设置请求头信息。在发送POST请求时，通常需要设置 <code>Content-type</code> 头信息，以告知服务器发送的数据类型。例如：</p>
            <pre>
                <code>
xhr.setRequestHeader("Content-type", "application/json");
let data = JSON.stringify({name: "Alice", age: 25});
xhr.send(data);
                </code>
            </pre>
        </li>
    </ol>

    <h2>三、Ajax请求的基本步骤</h2>
    <h3>（一）创建XMLHttpRequest对象</h3>
    <p>如上述创建对象部分所述，根据不同浏览器情况创建 <code>XMLHttpRequest</code> 对象。</p>
    <h3>（二）设置请求</h3>
    <p>使用 <code>open()</code> 方法设置请求的方法（GET或POST）、URL以及是否异步。例如：</p>
    <pre>
        <code>
let xhr = new XMLHttpRequest();
xhr.open("GET", "products.json", true);
        </code>
    </pre>
    <h3>（三）发送请求</h3>
    <p>使用 <code>send()</code> 方法发送请求。对于GET请求，<code>send()</code> 方法可不传入参数；对于POST请求，需要将数据作为参数传入。例如：</p>
    <pre>
        <code>
// GET请求
xhr.send();
// POST请求
let data = "productName=iPhone&price=999";
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.send(data);
        </code>
    </pre>
    <h3>（四）处理响应</h3>
    <p>通过监听 <code>readystatechange</code> 事件来处理服务器的响应。当 <code>readyState</code> 变为4且 <code>status</code> 为200时，表示请求成功，可获取响应数据。例如：</p>
    <pre>
        <code>
xhr.onreadystatechange = function () {
    if (this.readyState === 4 &amp;&amp; this.status === 200) {
        let response = JSON.parse(this.responseText);
        console.log(response);
        // 在这里对响应数据进行进一步处理，如更新页面元素等
    }
};
        </code>
    </pre>

    <h2>四、GET和POST请求</h2>
    <h3>（一）GET请求</h3>
    <ol>
        <li>
            <p><strong>特点</strong>：</p>
            <ul>
                <li>请求参数附加在URL后面，以"?参数名1=值1&参数名2=值2"的形式拼接。例如：<code>http://example.com/api?name=John&age=30</code>。</li>
                <li>数据长度有限制，因为URL的长度是有限的，不同浏览器对URL长度的限制不同，一般在2000个字符左右。</li>
                <li>安全性相对较低，因为参数暴露在URL中，可能会被缓存、记录在服务器日志等。</li>
                <li>适用于获取数据的操作，如从服务器获取文章列表、商品信息等。</li>
            </ul>
        </li>
        <li>
            <p><strong>示例代码</strong>：</p>
            <pre>
                <code>
let xhr = new XMLHttpRequest();
let url = "data.php?category=electronics";
xhr.open("GET", url, true);
xhr.onreadystatechange = function () {
    if (this.readyState === 4 &amp;&amp; this.status === 200) {
        let response = this.responseText;
        console.log(response);
    }
};
xhr.send();
                </code>
            </pre>
        </li>
    </ol>
    <h3>（二）POST请求</h3>
    <ol>
        <li>
            <p><strong>特点</strong>：</p>
            <ul>
                <li>请求参数放在请求体中，不会显示在URL中。</li>
                <li>数据长度理论上没有限制（受服务器配置影响）。</li>
                <li>安全性相对较高，因为参数不暴露在URL中。</li>
                <li>适用于向服务器提交数据的操作，如用户注册、登录、提交表单数据等。</li>
            </ul>
        </li>
        <li>
            <p><strong>示例代码</strong>：</p>
            <pre>
                <code>
let xhr = new XMLHttpRequest();
let url = "submit.php";
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
let data = "username=admin&password=123456";
xhr.onreadystatechange = function () {
    if (this.readyState === 4 &amp;&amp; this.status === 200) {
        let response = this.responseText;
        console.log(response);
    }
};
xhr.send(data);
                </code>
            </pre>
        </li>
    </ol>

    <h2>五、跨域问题</h2>
    <h3>（一）什么是跨域</h3>
    <p>当一个网页的请求URL的协议、域名、端口三者中有一个与当前页面的URL不同时，就会产生跨域问题。例如，当前页面的URL是 <code>http://localhost:8080</code>，而请求的URL是 <code>http://api.example.com</code>，这就属于跨域请求。由于浏览器的同源策略限制，跨域请求默认是不被允许的。</p>
    <h3>（二）解决方法</h3>
    <ol>
        <li>
            <p><strong>JSONP（JSON with Padding）</strong>：利用 <code>&lt;script&gt;</code> 标签不受同源策略限制的特点，通过动态创建 <code>&lt;script&gt;</code> 标签来进行跨域请求。服务器返回的数据需要包裹在一个回调函数中，前端在请求时需要指定回调函数名。例如：</p>
            <pre>
                <code>
&lt;script&gt;
    function handleResponse(response) {
        console.log(response);
    }
&lt;/script&gt;
&lt;script src="http://api.example.com/data?callback=handleResponse"&gt;&lt;/script&gt;
                </code>
            </pre>
            <p>在服务器端（以Node.js为例），代码可能如下：</p>
            <pre>
                <code>
const http = require('http');
const querystring = require('querystring');

http.createServer((req, res) => {
    let query = querystring.parse(req.url.split('?')[1]);
    let callback = query.callback;
    let data = {message: "这是跨域返回的数据"};
    res.writeHead(200, {'Content-Type': 'application/javascript'});
    res.end(callback + '(' + JSON.stringify(data) + ')');
}).listen(3000);
                </code>
            </pre>
        </li>
        <li>
            <p><strong>CORS（Cross - Origin Resource Sharing）</strong>：在服务器端设置响应头，允许特定的源进行跨域请求。例如在Node.js中使用Express框架，可通过中间件来实现：</p>
            <pre>
                <code>
const express = require('express');
const app = express();

app.use((req, res, next) => {
    res.setHeader('Access - Control - Allow - Origin', '*');
    res.setHeader('Access - Control - Allow - Methods', 'GET, POST, PUT, DELETE');
    res.setHeader('Access - Control - Allow - Headers', 'Content - Type');
    next();
});

app.get('/data', (req, res) => {
    res.json({message: "这是通过CORS返回的数据"});
});

app.listen(3000);
                </code>
            </pre>
            <p>其中，<code>Access - Control - Allow - Origin</code> 设置允许的源，<code>*</code> 表示允许所有源；<code>Access - Control - Allow - Methods</code> 设置允许的请求方法；<code>Access - Control - Allow - Headers</code> 设置允许的请求头。</p>
        </li>
    </ol>
</body>
</html>